package com.sduept.i1;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.ssssssss.magicapi.Const;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.core.TioConfig;
import org.tio.core.exception.TioDecodeException;
import org.tio.core.intf.Packet;
import org.tio.server.intf.TioServerHandler;

import com.sduept.i1.model.Apdu;
import com.sduept.i1.model.MonitorPacket;
import com.sduept.i1.parse.Decoder;
import com.sduept.tool.CrcUtil;
import com.sduept.tool.PrintTool;



/**
 * @author zcg
 */
//@Component
public class I1ServerHandler implements TioServerHandler {
	private static Logger logger = LoggerFactory.getLogger(I1ClientHandler.class);
   /**
     * 解码：把接收到的ByteBuffer，解码成应用可以识别的业务消息包
     * 总的消息结构：消息头 + 消息体
     * 消息头结构：    4个字节，存储消息体的长度
     * 消息体结构：   对象的json串的byte[]
     */
    @Override
    public MonitorPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws TioDecodeException {
    	logger.info("--------------------------I1ServerHandler.decode");
        if(readableLength < 4) {
            return null;
        }
        if(I1Server.bDebug) {
            // 数据处理，调用magic-api接口
            Map<String,Object> params = new HashMap<>();
            params.put("bytes",PrintTool.bytes2hexLines(buffer.array(), position, readableLength));
            params.put("info","I1协议-----接收");
            params.put("time",Const.df.format(new Date()));
            Const.getService().execute("POST","/network/revDebugData", params);
        }
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        int startHeader = buffer.getShort() & 0xFFFF; // 报文头 2Bytes 23205
        if(startHeader != MonitorPacket.FRAME_HEADER) {
            logger.error("服务端:开始帧头不对,size:{}-{}", readableLength, PrintTool.bytes2hex(buffer.array(), position, readableLength));
            Tio.close(channelContext, "开始帧头不对");
            return null;
        }
        int msgLen = buffer.getShort(); // 报文长度 2Bytes 61

        if(readableLength/*可读字节数*/ - 4/*已读字节数*/ < 23 + msgLen) {
            return null;
        }
        byte[] idBytes = new byte[17];
        buffer.get(idBytes); // 状态检测装置ID 17Bytes [...] 转成字符串后 Device1234567890a
        String identification = new String(idBytes);
        MonitorPacket monitorPacket = null;
        ChannelContext searchChannelContext = Tio.getByBsId(I1Server.getInstance().getServerTioConfig(), identification);
        if(searchChannelContext != null&&searchChannelContext == channelContext) {//找到上次连接，并且与形参连接一致
            monitorPacket = (MonitorPacket) channelContext.get(I1Server.MONITOR_PACKET_KEY);
        }
        else {
        	logger.info("--------------------------I1ServerHandler.decode: not find");
        	monitorPacket = new MonitorPacket();
        	channelContext.set(I1Server.MONITOR_PACKET_KEY, monitorPacket);
        }
        
        monitorPacket.identification = identification;
        monitorPacket.inApdu.msgLen = msgLen;
        monitorPacket.inApdu.frameType = buffer.get() & 0xFF; // 帧类型 1Byte 1
        monitorPacket.inApdu.msgType = buffer.get() & 0xFF; // 报文类型 1Byte 1
        monitorPacket.inApdu.frameNo = buffer.get() & 0xFF; // 帧序列号 1Byte 1
        
        if(msgLen > 0) {
            monitorPacket.inApdu.elements = new byte[msgLen];
            buffer.get(monitorPacket.inApdu.elements); // 报文内容 变长（报文长度）
        } else {
            monitorPacket.inApdu.elements = null;
        }
        
        int crc16 = buffer.getShort() & 0xFFFF; // 校验位 2Bytes 36657
        int crcCheck = CrcUtil.calcCrc16(buffer.array(), position + 2, msgLen + 22);

        if(crc16 != crcCheck) {
            logger.error("服务端:CRC16校验不对,msgLen={}-crc16={}-crcCheck={}", msgLen, crc16,crcCheck);
            Tio.close(channelContext, "CRC16校验不对");
            return null;
        }
        int frameEnd = buffer.get() & 0xFF; // 报文尾 1Byte 150
        if(frameEnd != MonitorPacket.FRAME_END) {
            Tio.close(channelContext, "结束帧尾不对");
            return null;
        }

// 		注意，输出报文，会影响报文解析，调试才能打开
//      buffer.clear();
//      logger.info("Server receive: " + PrintTool.bytes2hex(buffer.array(), position, msgLen + 27));
        return monitorPacket;
    }
    /**
     * 编码：把业务消息包编码为可以发送的ByteBuffer
     * 总的消息结构：消息头 + 消息体
     * 消息头结构：    4个字节，存储消息体的长度
     * 消息体结构：   对象的json串的byte[]
     */
    @Override
    public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
    	logger.info("--------------------------I1ServerHandler.encode");
        try {
            MonitorPacket monitorPacket = (MonitorPacket) packet;
            Apdu outApdu = monitorPacket.outApduQueue.poll();
            ByteBuffer byteBuffer = ByteBuffer.allocate(27 + outApdu.msgLen);
            byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
            byteBuffer.putShort(MonitorPacket.FRAME_HEADER); // 报文头 2Bytes
            byteBuffer.putShort((short) (outApdu.msgLen & 0xFFFF)); // 报文长度 2Bytes
            byteBuffer.put(monitorPacket.identification.getBytes()); // 状态检测装置ID 17Bytes
            byteBuffer.put((byte) (outApdu.frameType & 0xFF)); // 帧类型 1Byte
            byteBuffer.put((byte) (outApdu.msgType & 0xFF)); // 报文类型 1Byte
            byteBuffer.put((byte) (outApdu.frameNo & 0xFF)); // 帧序列号 1Byte
        
            if (outApdu.msgLen > 0) {
                byteBuffer.put(outApdu.elements); // 报文内容 长度等于msgLen
            }

            int crc16 = CrcUtil.calcCrc16(byteBuffer.array(), 2, 22 + outApdu.msgLen);
            byteBuffer.putShort((short) (crc16 & 0xFFFF)); // 校验位 2Bytes
            byteBuffer.put((byte) MonitorPacket.FRAME_END); // 报文尾 1Byte
               
            if(I1Server.bDebug) {
                // 数据处理，调用magic-api接口
                Map<String,Object> params = new HashMap<>();
                params.put("bytes",PrintTool.bytes2hexLines(byteBuffer.array(), 0, 27 + outApdu.msgLen));
                params.put("info","I1协议------发送");
                params.put("time",Const.df.format(new Date()));
                Const.getService().execute("POST","/network/revDebugData", params);
            }
            return byteBuffer;
        } catch (Exception e) {
            logger.error("encode异常", e);
            return null;
        }
    }
    /**
     * 处理消息
     */
    @Override
    public void handler(Packet packet, ChannelContext context){
        MonitorPacket monitorPacket = (MonitorPacket) packet;
        switch (monitorPacket.inApdu.frameType) {
            case 0x01:
            	logger.info("Receive Monitor Data Packet");
                Decoder.onMonitorDataPacket(monitorPacket, context);
                break;
            case 0x04:
            	logger.info("Receive Control Response Packet");
                Decoder.onControlResponsePacket(monitorPacket,context);
                break;
            case 0x05:
            	logger.info("Receive Remote Image Data Packet");
                Decoder.onRemoteImageDataPacket(monitorPacket, context);
                break;
            case 0x08:
            	logger.info("Receive Image Control Response Packet");
            	Decoder.onImageControlResponsePacket(monitorPacket, context);
                break;
            case 0x09:
            	logger.info("Receive Work State Packet");
                Decoder.onWorkStatePacket(monitorPacket, context);
                break;
            default:
                logger.error("Unknown FrameType: {}", monitorPacket.inApdu.frameType);
                break;
        }
    }
}